package com.wydexcel.generate.properties.generate;


import com.wydexcel.generate.properties.ExcelAbstractSheetProperties;
import com.wydexcel.generate.properties.ExcelFieldProperties;
import com.wydexcel.generate.properties.ExcelPositionProperties;
import com.wydexcel.generate.properties.s.ExcelCellBaseProperties;
import com.wydexcel.generate.properties.s.ExcelLinkProperties;
import com.wydexcel.generate.properties.s.ExcelRichTextCollectionProperties;
import com.wydexcel.generate.process.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;

import java.lang.reflect.Field;
import java.util.*;

public class ExcelGenerateImpl extends ExcelCommonProcess {


    private WydExcelSheetProcess sheetProcess = new DefaultWydExcelXSSFSheetProcess();
    private final WydExcelDynamicRowValueProcessImpl dynamicRowProcess = new WydExcelDynamicRowValueProcessImpl();
    private WydExcelDynamicRowValueProcessImpl.DynamicExcelRowProcess innerDynamicExcelRowProcess = null;
    private final WydExcelCellFontProcess cellFontProcess = new WydExcelCellFontProcessImpl();
    private final WydExcelCellBaseProcess baseProcess = new WydExcelCellBaseProcessImpl();
    private final WydExcelCellLinkerProcess linkerProcess = new WydExcelCellLinkerProcessImpl();
    private final WydExcelCellFormatProcess formatProcess = new WydExcelCellFormatProcessImpl();
    private final WydExcelCellCommentProcess cellCommentProcess = new WydExcelCellCommentProcessImpl();
    private final WydExcelRichTextProcess richTextProcess = new WydExcelRichTextProcessImpl();
    private final WydExcelCellMergeProcess mergeProcess = new WydExcelCellMergeProcessImpl();
    private final Map<String, CellStyle> cellStyleMap = new HashMap<>();

    private ExcelAbstractSheetProperties conf;

    public ExcelGenerateImpl(ExcelAbstractSheetProperties conf, Workbook workbook) {
        super(workbook);
        this.conf = conf;
        conf.build();

        create();
    }

    /**
     * 切换下一行处理器
     */
    public void nextRowProcess() {
        innerDynamicExcelRowProcess = dynamicRowProcess.dynamicExcelRowProcess.next;
    }


    //动态行，动态表头
    private Sheet create() {
        Sheet sheet = getSheet(conf.getSheetName());

        sheetProcess.afterExportProcess(sheet, conf);
        List<ExcelFieldProperties> cells = conf.getCells();

        for (ExcelFieldProperties headCell : cells) {
            dynamicRowProcess.put(headCell);
        }

        dynamicRowProcess.init();
        innerDynamicExcelRowProcess = dynamicRowProcess.dynamicExcelRowProcess;
        WydExcelDynamicRowValueProcessImpl.DynamicExcelRowProcess dynamicExcelRowProcess = dynamicRowProcess.dynamicExcelRowProcess;
        while (dynamicExcelRowProcess != null) {
            for (Map.Entry<String, ExcelFieldProperties> entry : dynamicExcelRowProcess.getFieldList().entrySet()) {
                ExcelFieldProperties headCell = entry.getValue();
                doGenerate(headCell, headCell.getValue());
            }
            dynamicExcelRowProcess.incrementIndex();
            dynamicExcelRowProcess = dynamicExcelRowProcess.next;
        }
        return sheet;
    }


    public ExcelGenerateImpl(ExcelAbstractSheetProperties conf, WydExcelCellValueProcess wydExcelCellValueProcess
            , WydExcelRowValueProcess wydExcelRowValueProcess, WydExcelSheetProcess sheetProcess, Workbook workbook) {
        super(workbook);
        this.conf = conf;
        this.sheetProcess = sheetProcess;
        create();
    }

    private final Map<Class, Field[]> classMap = new HashMap<>();

    /**
     * push 对象到方法中，
     * 方法会生成map
     *
     * @param o 对象信息 任意类型
     */
    public void generate(Object o) {
        if (null == o) {
            return;
        }
        Map<String, Object> map = new HashMap<>();
        Class<?> aClass = o.getClass();
        Field[] fields = classMap.computeIfAbsent(aClass, Class::getDeclaredFields);

        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(o);
                if (null != value) {
                    map.put(field.getName(), value + "");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        generate(map);
        map = null;
    }


    public void generate(Map<String, Object> map) {
        //此处conf 表示的是 对于 sheet的配置 ,这个地方不能每次都调用，应该单独抽取出去
        if (null == map || map.isEmpty()) {
            return;
        }
        while (innerDynamicExcelRowProcess != null) {
            int endIndex = innerDynamicExcelRowProcess.getEndIndex();
            Row row = getRow(conf.getSheetName(), endIndex);
            for (Map.Entry<String, ExcelFieldProperties> entry : innerDynamicExcelRowProcess.getFieldList().entrySet()) {
                String k = entry.getKey();
                ExcelFieldProperties properties = entry.getValue();
                if (null == properties) {
                    continue;
                }
                Object value = map.get(k);
                if (null == value || value.equals("")) {
                    continue;
                }
                doGenerate(properties, value, row, true);
            }
            innerDynamicExcelRowProcess.incrementIndex();
            return;
        }
    }


    public void mergeGenerate(List<Map<String, Object>> dataList) {
        //此处conf 表示的是 对于 sheet的配置 ,这个地方不能每次都调用，应该单独抽取出去
        if (null == dataList || dataList.isEmpty()) {
            return;
        }
        while (innerDynamicExcelRowProcess != null) {
            Map<String, ExcelFieldProperties> fieldList = innerDynamicExcelRowProcess.getFieldList();
//            if (dataList.size() == 1) {
//                Map<String, Object> map = dataList.get(0);
//                generate(map);
//                return;
//            }
            int mergeStartIndex = innerDynamicExcelRowProcess.getMergeStartIndexWith();
            int mergeEndIndex = innerDynamicExcelRowProcess.getMereEndIndex(dataList.size());// 合并的范围
            int rowIsGenerate = 0;
            for (int i = 0; i < dataList.size(); i++) {
                Map<String, Object> map = dataList.get(i);
                int startIndex = innerDynamicExcelRowProcess.getEndIndex();//开始合并的地方
                Row row = getRow(conf.getSheetName(), startIndex);
                for (Map.Entry<String, Object> e : map.entrySet()) {
                    String k = e.getKey();
                    Object value = e.getValue();
                    ExcelFieldProperties properties = fieldList.get(k);
                    if (null == properties) {

                        continue;
                    }
                    if (null == value || value.equals("")) {
                        value = "";
                    }
                    ExcelPositionProperties positionProperties = this.conf.getPositionProperties(k, true);
                    if (null != positionProperties) {
                        ExcelCellBaseProperties baseProperties = this.conf.getBaseMap(k, true);
                        positionProperties.setFirstRow(mergeStartIndex);
                        positionProperties.setLastRow(mergeEndIndex);
                        Integer firstCol = positionProperties.getFirstCol();
                        Integer lastCol = positionProperties.getLastCol();
                        if (null == firstCol) {
                            positionProperties.setFirstCol(properties.getExcelStartCellIndex());
                        }
                        if (null == lastCol) {
                            positionProperties.setLastCol(properties.getExcelStartCellIndex());
                        }
                        mergeProcess.mergeProcess(positionProperties, baseProperties, getSheet(this.conf.getSheetName()), true);
                        if (rowIsGenerate == 0) {
                            doGenerate(properties, value, row, true);
                        }
                        continue;
                    }
                    doGenerate(properties, value, row, true);
                }
                innerDynamicExcelRowProcess.incrementIndex();
                rowIsGenerate = 1;
            }

            return;
        }
    }


    private void doGenerate(ExcelFieldProperties fieldProperties, String value) {
        Row headerRow = getRow(conf.getSheetName(), fieldProperties.getExcelStartRowIndex());
        doGenerate(fieldProperties, value, headerRow, false);
    }

    private void doGenerate(ExcelFieldProperties fieldProperties, Object value, Row row, boolean isBody) {

        Cell cell = getCell(row, fieldProperties.getExcelStartCellIndex());
        String excelFieldName = fieldProperties.getExcelFieldName();
        String key = excelFieldName + (isBody ? "2" : "1");
        //4.超链接处理
        ExcelLinkProperties linkProperties = conf.linksMap.get(key);
        //todo 自定义链接怎么处理
        linkerProcess.linkerProcess(row, cell, linkProperties, isBody);
        //6.批注处理
        cellCommentProcess.commentProcess(row, cell, conf.commentMap.get(key), isBody);
        CellStyle cellStyle = cellStyleMap.get(key);
        if (null == cellStyle) {
            //1.基本信息处理
            cellStyle = baseProcess.baseProcess(row, cell, conf.baseMap.get(key), excelFieldName, isBody);
            Font font = cellFontProcess.fontProcess(row, cell, conf.fontMap.get(key), excelFieldName, isBody);
            if (null != font) {
                cellStyle.setFont(font);
            }
            //5.格式化处理
            Short dataformat = formatProcess.formatProcess(row, cell, conf.dataFormatMap.get(key), isBody);
            if (null != dataformat) {
                cellStyle.setDataFormat(dataformat);
                if (value instanceof Double) {
                    cell.setCellValue((Double) value);
                    return;
                }
                cell.setCellValue(Double.parseDouble((String) value));
                return;
            }
            cellStyleMap.put(key, cellStyle);
        }
        cell.setCellStyle(cellStyle);


        // 是否与格式化互斥
        ExcelRichTextCollectionProperties richTextProperties = conf.getExcelRichTextCollectionProperties(key, isBody, value.toString());
        if (null != richTextProperties) {
            XSSFRichTextString richTextString = richTextProcess.richTextProcess(row, cell, getSheet(conf.getSheetName()), richTextProperties, isBody);
            if (null != richTextString) {
                cell.setCellValue(richTextString);
                return;
            }
        }

        if (value instanceof String) {
            cell.setCellValue((String) value);
        }
    }


    public void writeToDisk(String path) {
        write(path);
    }

    public void processSheet() {
        Sheet sheet = getSheet(conf.getSheetName());
        sheetProcess.afterExportProcessSheet(sheet, conf);
    }
}

